package com.google.code.mochaccino.framework.util;
/*
 * Copyright 2012 Claude Houle claude.houle@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;

/**
 * A set of one-call method to compress/decompress information out of numerous sources such as bytes
 * arrays or files or streams
 */

public final class CompressionUtil {

	private CompressionUtil() {
	}

	/** Compression Method */

	public static File compress( File file ) {
		try {
			File compressedFile = new File( file.getName() + ".compressed" );
			compress( new FileInputStream( file ), new FileOutputStream( compressedFile ) );
			return compressedFile;
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void compress( File file, OutputStream out ) {
		try {
			compress( new FileInputStream( file ), out );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void compress( InputStream in, OutputStream out ) {
		try {
			BufferedInputStream bin = new BufferedInputStream( in );
			BufferedOutputStream bout = new BufferedOutputStream( new DeflaterOutputStream( out, new Deflater( Deflater.BEST_COMPRESSION ) ) );
			int wB = -1;
			while ( (wB = bin.read()) != -1 ) {
				bout.write( wB );
			}
			IOUtils.closeQuietly( bout );
			IOUtils.closeQuietly( bin );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static String compress( String content ) {
		return new String( compress( content.getBytes() ) );
	}

	/** Compression Method */

	public static byte[] compress( byte[] content ) {
		ByteArrayInputStream in = new ByteArrayInputStream( content );
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		compress( in, out );
		return out.toByteArray();
	}

	/** Decompression Method */

	public static byte[] decompress( File conten ) {
		try {
			FileInputStream in = new FileInputStream( conten );
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			decompress( in, out );
			return out.toByteArray();
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Decompression Method */

	public static InputStream decompress( InputStream in ) {
		return new BufferedInputStream( new InflaterInputStream( in, new Inflater() ) );
	}

	/** Decompression Method */

	public static void decompress( InputStream in, OutputStream out ) {
		try {
			BufferedInputStream bin = new BufferedInputStream( new InflaterInputStream( in, new Inflater() ) );
			BufferedOutputStream bout = new BufferedOutputStream( out );
			int wB = -1;
			while ( (wB = bin.read()) != -1 ) {
				bout.write( wB );
			}
			IOUtils.closeQuietly( bout );
			IOUtils.closeQuietly( bin );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Decompression Method */

	public static String decompress( String conten ) {
		return new String( decompress( conten.getBytes() ) );
	}

	/** Decompression Method */

	public static byte[] decompress( byte[] content ) {
		ByteArrayInputStream in = new ByteArrayInputStream( content );
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		decompress( in, out );
		return out.toByteArray();
	}

	/** Decompression Method */

	public static InputStream gunzip( byte[] in ) {
		return gunzip( new ByteArrayInputStream( in ) );
	}

	/** Decompression Method */

	public static InputStream gunzip( File in ) {
		try {
			return gunzip( new FileInputStream( in ) );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Decompression Method */

	public static InputStream gunzip( InputStream in ) {
		try {
			return new GZIPInputStream( in );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Decompression Method */
	public static void gunzip( InputStream input, OutputStream content ) {
		BufferedOutputStream out = null;
		BufferedInputStream in = null;
		try {
			out = new BufferedOutputStream( content );
			in = new BufferedInputStream( new GZIPInputStream( input ) );
			int b = -1;
			while ( (b = in.read()) != -1 ) {
				out.write( b );
			}
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		} finally {
			IOUtils.closeQuietly( out );
			IOUtils.closeQuietly( in );
		}
	}

	/** Compression Method */

	public static void gzip( byte[] in, OutputStream out ) {
		gzip( new ByteArrayInputStream( in ), out );
	}

	/** Compression Method */

	public static File gzip( File content ) {
		try {
			File file = new File( content.getAbsolutePath() + ".gz" );
			gzip( new FileInputStream( content ), new FileOutputStream( file ) );
			return file;
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void gzip( File content, OutputStream out ) {
		try {
			gzip( new FileInputStream( content ), out );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void gzip( InputStream input, OutputStream output ) {
		GZIPOutputStream out = null;
		BufferedInputStream in = null;
		try {
			out = new GZIPOutputStream( output );
			in = new BufferedInputStream( input );
			int b = -1;
			while ( (b = in.read()) != -1 ) {
				out.write( b );
			}
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		} finally {
			IOUtils.closeQuietly( out );
			IOUtils.closeQuietly( in );
		}
	}

	/** Compression Method */

	public static File jar( File content ) {
		try {
			File file = File.createTempFile( "CompressionUtil-", ".jar" );
			jar( content, file );
			return file;
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void jar( File content, File output ) {
		jar( new File[] { content }, output );
	}

	/** Compression Method */

	public static File jar( File[] contents ) {
		try {
			File wFile = File.createTempFile( "CompressionUtil-", ".jar" );
			jar( contents, wFile );
			return wFile;
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */
	public static void jar( File[] content, File output ) {
		try {
			jar( content, new FileOutputStream( output ) );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void jar( File[] contents, OutputStream output ) {
		JarOutputStream out = null;
		BufferedInputStream in = null;
		try {
			out = new JarOutputStream( new BufferedOutputStream( output ) );
			for ( File content : contents ) {
				out.putNextEntry( new JarEntry( content.getName() ) );
				try {
					in = new BufferedInputStream( new FileInputStream( content ) );
					int b = -1;
					while ( (b = in.read()) != -1 ) {
						out.write( b );
					}
				} finally {
					if ( in != null ) {
						in.close();
					}
				}
				out.closeEntry();
			}
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		} finally {
			IOUtils.closeQuietly( out );
		}
	}

	/** Unjar */
	public static List<File> unjar( File file ) {
		try {
			return unjar( new FileInputStream( file ) );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Un Jar */
	public static List<File> unjar( InputStream content ) throws IOException {
		return unzipOrUnjar( new JarInputStream( new BufferedInputStream( content ) ) );
	}

	private static List<File> unzipOrUnjar( ZipInputStream in ) throws IOException {
		List<File> fileList = new ArrayList<File>();
		BufferedOutputStream out = null;
		ZipEntry entry = null;
		try {
			while ( (entry = in.getNextEntry()) != null ) {
				File file = new File( entry.getName() );
				try {
					out = new BufferedOutputStream( new FileOutputStream( file ) );
					while ( in.available() != 0 ) {
						out.write( in.read() );
					}
				} finally {
					IOUtils.closeQuietly( out );
				}
				fileList.add( file );
			}
		} finally {
			IOUtils.closeQuietly( out );
			IOUtils.closeQuietly( in );
		}
		return fileList;
	}

	/** Decompression Method */

	public static List<File> unzip( File file ) {
		try {
			return unzip( new FileInputStream( file ) );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Decompression Method */

	public static List<File> unzip( InputStream input ) throws IOException {
		return unzipOrUnjar( new ZipInputStream( new BufferedInputStream( input ) ) );
	}

	/** Compression Method */

	public static File zip( File content ) {
		try {
			File wFile = File.createTempFile( "CompressionUtil-", ".zip" );
			zip( content, wFile );
			return wFile;
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void zip( File contents, File output ) {
		zip( new File[] { contents }, output );
	}

	/** Compression Method */

	public static File zip( File[] contents ) {
		try {
			File output = File.createTempFile( "CompressionUtil-", ".zip" );
			zip( contents, output );
			return output;
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */

	public static void zip( File[] content, File output ) {
		try {
			zip( content, new FileOutputStream( output ) );
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		}
	}

	/** Compression Method */
	public static void zip( File[] contents, OutputStream output ) {
		ZipOutputStream out = null;
		BufferedInputStream in = null;
		try {
			out = new ZipOutputStream( new BufferedOutputStream( output ) );
			for ( File wElement : contents ) {
				out.putNextEntry( new ZipEntry( wElement.getName() ) );
				try {
					in = new BufferedInputStream( new FileInputStream( wElement ) );
					int b = -1;
					while ( (b = in.read()) != -1 ) {
						out.write( b );
					}
				} finally {
					if ( in != null ) {
						in.close();
					}
				}
				out.closeEntry();
			}
		} catch ( IOException e ) {
			throw new RuntimeException( e );
		} finally {
			IOUtils.closeQuietly( out );
			IOUtils.closeQuietly( in );
		}
	}
}